package unit_test_demo

import (
	sqle "github.com/dolthub/go-mysql-server"
	"github.com/dolthub/go-mysql-server/sql"
	"github.com/dolthub/go-mysql-server/sql/mysql_db"
)

// Copyright 2022 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This file walks through how an application might add support for users through the inclusion of the "mysql" database.
// By default, this database is disabled, and must be either explicitly enabled, or implicitly enabled by loading into
// it. When disabled, all users are accepted, meaning that anyone may connect to your application. This is great for
// testing, but not so great for an exposed application that wants to protect itself from malicious actors.

var (
	// When set to true, calls "enableUserAccounts".
	enableUsers = false

	// When set to false, we create an account named "root" that has all privileges and does not have a password set.
	// When set to true, we create an account named "gms_user" that has all privileges and has the password "123456".
	// This is a stand-in for checking whether a file exists that contains the "mysql" database's data.
	pretendThatFileExists = false
)

// MySQLPersister is an example struct which handles the persistence of the data in the "mysql" database.
type MySQLPersister struct {
	Data []byte
}

var _ mysql_db.MySQLDbPersistence = (*MySQLPersister)(nil)

// Persist implements the interface mysql_db.MySQLDbPersistence. This function is simple, in that it simply stores
// the given data inside itself. A real application would persist to the file system.
func (m *MySQLPersister) Persist(ctx *sql.Context, data []byte) error {
	m.Data = data
	return nil
}

func enableUserAccounts(ctx *sql.Context, engine *sqle.Engine) error {
	mysqlDb := engine.Analyzer.Catalog.MySQLDb

	// The functions "AddRootAccount" and "LoadData" both automatically enable the "mysql" database, but this is just
	// to explicitly show how one can manually enable (or disable) the database.
	mysqlDb.SetEnabled(true)

	// The persister here simply stands-in for your provided persistence function. The database calls this whenever it
	// needs to save any changes to any of the "mysql" database's tables. The memory session persists in memory,
	// but can be swapped out with the lines below
	// persister := &MySQLPersister{}
	// mysqlDb.SetPersister(persister)

	// Here we show how a real application may choose to bootstrap their users. If we've previously created a file
	// (generated by calling the above persister), then we may check that the file exists, and load the file if it does.
	// If the file does not exist, then we create a "root" account, so that we may create our default users. Do remember
	// to either remove the "root" account when done, or give it a password, otherwise you'll have an all-powerful
	// account without any protection.
	if pretendThatFileExists {
		dataLoadedFromPretendFile := createLoadedData()
		if err := mysqlDb.LoadData(ctx, dataLoadedFromPretendFile); err != nil {
			return err
		}
	} else {
		// AddRootAccount creates a password-less account named "root" that has all privileges. This is intended for use
		// with testing, and also to set up the initial user accounts. A real application may want to check that a
		// persisted file exists, and call "LoadData" if one does. If a file does not exist, it would call
		// "AddRootAccount".
		mysqlDb.AddRootAccount()
	}

	return nil
}

// createLoadedData returns data that would match what a file would contain if you had a single user named "gms_user"
// (on "localhost") that had all privileges, along with the password "123456".
func createLoadedData() []byte {
	// As we continue development on go-mysql-server, our file format will continue to evolve. We guarantee backward
	// compatibility for persisted "mysql" databases, so the below data will always work. This is only here for example
	// purposes.
	return []byte{
		16, 0, 0, 0, 0, 0, 0, 0, 8, 0, 12, 0, 8, 0, 4, 0, 8, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
		28, 0, 0, 0, 0, 0, 22, 0, 40, 0, 36, 0, 32, 0, 28, 0, 24, 0, 20, 0, 8, 0, 0, 0, 0, 0, 4, 0, 22, 0, 0, 0, 36, 0,
		0, 0, 168, 231, 96, 99, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 72, 0, 0, 0, 104, 0, 0, 0, 248, 0, 0, 0, 4, 1, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 42, 54, 66, 66, 52, 56, 51, 55, 69, 66, 55, 52, 51, 50, 57, 49, 48, 53,
		69, 69, 52, 53, 54, 56, 68, 68, 65, 55, 68, 67, 54, 55, 69, 68, 50, 67, 65, 50, 65, 68, 57, 0, 0, 0, 21, 0, 0,
		0, 109, 121, 115, 113, 108, 95, 110, 97, 116, 105, 118, 101, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 10,
		0, 16, 0, 12, 0, 8, 0, 4, 0, 10, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,
		0, 0, 23, 0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 24, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 22, 0, 0, 0, 21, 0,
		0, 0, 18, 0, 0, 0, 15, 0, 0, 0, 29, 0, 0, 0, 27, 0, 0, 0, 11, 0, 0, 0, 28, 0, 0, 0, 26, 0, 0, 0, 5, 0, 0, 0, 4,
		0, 0, 0, 2, 0, 0, 0, 30, 0, 0, 0, 25, 0, 0, 0, 20, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 13, 0, 0, 0, 17, 0, 0, 0,
		9, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 0, 0,
		8, 0, 0, 0, 103, 109, 115, 95, 117, 115, 101, 114, 0, 0, 0, 0,
	}
}
